﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;    /* for file io */
using System.Xml.Serialization; /* for xml serialization */

namespace AcmeSportsStore
{
    public class AccountsStore
    {
        private enum CompPasswordResult
        {
            Equal,
            AccountNotExist,
            PasswordError
        }

        public enum NewPasswordResult
        {
            Successful,
            WrongOldPassword,
            TwoPasswordsNotSame,
        }

        [Serializable()]
        public class UserDetails
        {
            public string Password;
            public string DataSetFilePath;

            public UserDetails()
            {
                Password = cDefAccPwd;
                DataSetFilePath = cDefAccDsFilePath;
            }

            public UserDetails(string password, string datasetFilePath)
            {
                Password = password;
                DataSetFilePath = datasetFilePath;
            }
        }

        /*
         * to make it xml serializable,
         * the struct is defined public and serializable
         */
        [Serializable()]
        public struct AccountInfo
        {
            /*
             * map from user's accountname to the encoded password
             */
            public SerializableDictionary<string, UserDetails> UserPasswordMap;
        }

        /*
         * </code>
         */

        private static AccountInfo sAccountInfo;
        private const string cDefAccName = "GUEST";
        private const string cDefAccPwd = "";
        private const string cDefAccDsFilePath = "";
        private const bool cIgnoreCase = true;

        private static string sCurrentUser = null;

        public static string DataSetFilePath
        {
            get
            {
                return sAccountInfo.UserPasswordMap[sCurrentUser].DataSetFilePath;
            }

            set
            {
                sAccountInfo.UserPasswordMap[sCurrentUser].DataSetFilePath = value;
                SaveAccountInfoToFile();
            }
        }


        public static void LoadAccountInfoFromFile()
        {
            /*
             * <code remarks = "load the map">
             */

            string pwdFilePath = System.Reflection.Assembly.GetExecutingAssembly().Location;
            pwdFilePath = Path.GetDirectoryName(pwdFilePath);
            pwdFilePath = Path.Combine(pwdFilePath, "passwords.xml");

            try
            {
                using (StreamReader sr = new StreamReader(pwdFilePath))
                {
                    XmlSerializer xmlSerializer = new XmlSerializer(typeof(AccountInfo));
                    sAccountInfo = (AccountInfo)xmlSerializer.Deserialize(sr);
                }
            }
            catch (FileNotFoundException)
            {   /* file not found */
                sAccountInfo.UserPasswordMap = new SerializableDictionary<string, UserDetails>();
                sAccountInfo.UserPasswordMap.Add(cDefAccName, 
                    new UserDetails(Encode(cDefAccPwd), ""));
                SaveAccountInfoToFile();
            }
            catch (InvalidOperationException)
            {   /* bad xml file*/
                sAccountInfo.UserPasswordMap = new SerializableDictionary<string, UserDetails>();
                sAccountInfo.UserPasswordMap.Add(cDefAccName,
                    new UserDetails(Encode(cDefAccPwd), ""));
                SaveAccountInfoToFile();
            }

            /*
             * </code>
             */
        }

        static void SaveAccountInfoToFile()
        {
            /*
             * <code remarks = "save the dictionary">
             */

            string pwdFilePath = System.Reflection.Assembly.GetExecutingAssembly().Location;
            pwdFilePath = Path.GetDirectoryName(pwdFilePath);
            pwdFilePath = Path.Combine(pwdFilePath, "passwords.xml");

            try
            {
                using (StreamWriter sw = new StreamWriter(pwdFilePath))
                {
                    XmlSerializer xmlSerializer = new XmlSerializer(typeof(AccountInfo));
                    xmlSerializer.Serialize(sw, sAccountInfo);
                }
            }
            catch (Exception)
            {
                System.Windows.Forms.MessageBox.Show("Failed to save accounts to file", "Error");
            }

            /*
             * </code>
             */
        }


        /*
         *   Encode the password
         */
        private static string Encode(string password)
        {
            char[] chr = new char[password.Length];
            for (int i = 0; i < password.Length; i++)
            {
                chr[i] = Convert.ToChar(Convert.ToInt32(password[i]) + 1);
            }
            return (new string(chr));
        }
        /*
         * Decode the password
         */
        private static string Decode(string password)
        {
            char[] chr = new char[password.Length];
            for (int i = 0; i < password.Length; i++)
            {
                chr[i] = Convert.ToChar(Convert.ToInt32(password[i]) - 1);
            }
            return (new string(chr));
        }

        private static CompPasswordResult CompPassword(string name, string getPassword)
        {
            if (!sAccountInfo.UserPasswordMap.ContainsKey(name))
            {
                return CompPasswordResult.AccountNotExist;
            }

            string encodedPassword = sAccountInfo.UserPasswordMap[name].Password;
            string password = Decode(encodedPassword);

            if (encodedPassword == null || password.Equals(getPassword))
                return CompPasswordResult.Equal;
            else
                return CompPasswordResult.PasswordError;
        }

        public static bool TryLogin(string name, string getPassword)
        {
            /*
             * the case of the account name is ignored
             */
            name = name.ToUpper();

            bool res = CompPassword(name, getPassword) 
                == CompPasswordResult.Equal;
            if (res)
            {
                sCurrentUser = name;
            }
            else
            {
                sCurrentUser = null;
            }
            return res;
        }

        public static NewPasswordResult NewPassword(string name,
            string oldCode, string newCode1, string newCode2)
        {
            /*
             * the case of the account name is ignored
             */
            name = name.ToUpper();

            CompPasswordResult cpr = CompPassword(name, oldCode);
            if (cpr == CompPasswordResult.PasswordError)
            {
                return NewPasswordResult.WrongOldPassword;
            }

            if (newCode1.Equals(newCode2))
            {
                string encodedPassword = Encode(newCode1);

                if (sAccountInfo.UserPasswordMap.ContainsKey(name))
                {   /* the account exists */
                    sAccountInfo.UserPasswordMap[name].Password = encodedPassword;
                }
                else
                {
                    sAccountInfo.UserPasswordMap[name] = new UserDetails(
                        encodedPassword, cDefAccDsFilePath);
                }

                SaveAccountInfoToFile();
                return NewPasswordResult.Successful;
            }
            else
            {
                return NewPasswordResult.TwoPasswordsNotSame;
            }
        }
    }
}
